package com.gaga.console.core.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import com.gaga.common.constant.BvConstants;
import com.gaga.common.enums.SingleDoubleModelType;
import com.gaga.common.exception.BusException;
import com.gaga.console.core.service.*;
import com.gaga.metadata.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author liujia
 * @since 2025-04-06
 */
@Slf4j
@Service
public class AssistantServiceImpl implements AssistantService {

    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private BvAudioPlayService bvAudioPlayService;

    @Autowired
    private BvConfigService bvConfigService;

    @Autowired
    private BvScriptCategoryService bvScriptCategoryService;

    @Override
    public void openBarrageAssistant() throws FileNotFoundException {
        String staticClasspath = ResourceUtils.getURL("classpath:").getPath();
        String barrageAssistantPath = sysConfigService.getAbsolutePathByKey(BvConstants.BARRAGE_ASSISTANT_PATH);
        if (StringUtils.isEmpty(barrageAssistantPath)) {
            throw new BusException("请先配置弹幕助手路径");
        }

        exec(barrageAssistantPath, "弹幕助手", true, false);
    }

    @Override
    public void startModel() {

        String modelPath9876 = sysConfigService.getAbsolutePathByKey(BvConstants.MODEL_BAT_PATH_9876);
        String modelPath9875 = sysConfigService.getAbsolutePathByKey(BvConstants.MODEL_BAT_PATH_9875);
        String modelPath9881 = sysConfigService.getAbsolutePathByKey(BvConstants.MODEL_BAT_PATH_9881);
        log.info("================modelPath9876=" + modelPath9876);
        log.info("================modelPath9875=" + modelPath9875);
        log.info("================modelPath9881=" + modelPath9881);
        if (StringUtils.isEmpty(modelPath9876) || StringUtils.isEmpty(modelPath9875) || StringUtils.isEmpty(modelPath9881)) {
            throw new BusException("请先配置模型路径");
        }

        String modelWorkspacePath = sysConfigService.getAbsolutePathByKey(BvConstants.MODEL_WORKSPACE_PATH);
        if (StringUtils.isEmpty(modelWorkspacePath)) {
            throw new BusException("请先配置模型工作路径");
        }

        try {
            killProcessByPort(9880);
        } catch (IOException e) {
            log.error("关闭其他模型失败：{}", e.getMessage());
        } catch (InterruptedException e) {
            log.error("关闭其他模型失败：{}", e.getMessage());
        }

//        if (isModelRunning()) {
//            return;
//        }
        String openDoubleModelStr = sysConfigService.getConfigValueByKey(BvConstants.SINGLE_DOUBLE_MODEL);
        if (SingleDoubleModelType.SINGLE.getCode().equals(openDoubleModelStr)) {
            shutDownModel("9875");
            shutDownModel("9881");

            if (!isModelRunning("9876")) {
                exec(modelPath9876, "模型", false, true);
            }

        } else if (SingleDoubleModelType.DOUBLE.getCode().equals(openDoubleModelStr)) {
            shutDownModel("9881");
            if (!isModelRunning("9875")) {
                exec(modelPath9875, "模型", false, true);
            }

            if (!isModelRunning("9876")) {
                exec(modelPath9876, "模型", false, true);
            }

        } else if (SingleDoubleModelType.COMPATIBLE.getCode().equals(openDoubleModelStr)) {
            shutDownModel("9875");
            shutDownModel("9876");
            if (!isModelRunning("9881")) {
                exec(modelPath9881, "模型", false, true);
            }
        }
    }

    @Override
    public void restartModel() {

        shutDownModel("9875");
        shutDownModel("9876");
        shutDownModel("9881");
        
        ThreadUtil.sleep(10000);

        startModel();
    }

    @Override
    public void shutDownModel() {

        String port = sysConfigService.getConfigValueByKey(BvConstants.MODEL_RUN_PORT);
        if (StringUtils.isEmpty(port)) {
            throw new BusException("请先配置模型端口");
        }
        String[] split = port.split(",");
        for (String p : split) {
            try {
                killProcessByPort(Integer.valueOf(p).intValue());
            } catch (IOException e) {
                log.error("关闭模型失败：{}", e.getMessage());
            } catch (InterruptedException e) {
                log.error("关闭模型失败：{}", e.getMessage());
            }
        }
    }

    @Override
    public void shutDownModel(String port) {

        if (StringUtils.isEmpty(port)) {
            throw new BusException("参数错误");
        }
        try {
            killProcessByPort(Integer.valueOf(port).intValue());
        } catch (IOException e) {
            log.error("关闭模型失败：{}", e.getMessage());
        } catch (InterruptedException e) {
            log.error("关闭模型失败：{}", e.getMessage());
        }
    }

    @Override
    public Boolean canRunModel() {
        List<String> pids = new ArrayList<>();
        String port = sysConfigService.getConfigValueByKey(BvConstants.MODEL_RUN_PORT);
        if (StringUtils.isEmpty(port)) {
            throw new BusException("请先配置模型端口");
        }
        String[] split = port.split(",");
        for (String p : split) {
            try {
                pids.addAll(findPidByPort(Integer.valueOf(p).intValue()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        if (pids.isEmpty()) {
            return true;
        }
        Optional<String> first = pids.stream().filter(pid -> pid.equals("0")).findFirst();
        if (first.isPresent()) {
            return false;
        }
        return true;
    }

    @Override
    public void openFolder(String filePath) throws IOException {
        String osName = System.getProperty("os.name", "");// 获取操作系统的名字
        log.info("{}命令打开文件{}", osName, filePath);
        if (osName.toLowerCase().contains("windows")) {
            String command = "cmd /c start \"\" \"" + filePath + "\"";
            Runtime.getRuntime().exec(command);
        } else if (osName.toLowerCase().replaceAll(" ", "").contains("macos")) {
            String command = "open '" + filePath + "'"; // 适用于Mac OS
            Runtime.getRuntime().exec(command);
        } else if (osName.toLowerCase().contains("linux")) {
            String command = "xdg-open '" + filePath + "'"; // 适用于Linux
            Runtime.getRuntime().exec(command);
        } else {
            log.error("不支持的系统:{}", osName);
        }
    }

    @Override
    public void clearLog() {
        String playLogFilePath = sysConfigService.getAbsolutePathByKey(BvConstants.PLAY_LOG_FILE_PATH);
        String generateAudioPath = sysConfigService.getAbsolutePathByKey(BvConstants.GENERATE_AUDIO_PATH);

        // 清理播放日志文件夹
        if (StringUtils.isNotBlank(playLogFilePath)) {
            log.info("开始清理播放日志文件夹: {}", playLogFilePath);
            clearDirectory(new File(playLogFilePath));
        }

        // 清理生成的音频文件夹
        if (StringUtils.isNotBlank(generateAudioPath)) {
            log.info("开始清理生成的音频文件夹: {}", generateAudioPath);
            clearDirectory(new File(generateAudioPath));
        }
    }

    /**
     * 清理目录下的所有文件和子目录
     *
     * @param directory 要清理的目录
     */
    private void clearDirectory(File directory) {
        if (!directory.exists()) {
            log.warn("目录不存在: {}", directory.getAbsolutePath());
            return;
        }

        if (!directory.isDirectory()) {
            log.warn("路径不是目录: {}", directory.getAbsolutePath());
            return;
        }

        try {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        clearDirectory(file);
                        if (!file.delete()) {
                            log.warn("无法删除目录: {}", file.getAbsolutePath());
                        } else {
                            log.info("已删除目录: {}", file.getAbsolutePath());
                        }
                    } else {
                        if (!file.delete()) {
                            log.warn("无法删除文件: {}", file.getAbsolutePath());
                        } else {
                            log.info("已删除文件: {}", file.getAbsolutePath());
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("清理目录时发生错误: {}", directory.getAbsolutePath(), e);
        }
    }

    @Override
    public Boolean isModelRunning() {
        List<String> pids = new ArrayList<>();
        String port = sysConfigService.getConfigValueByKey(BvConstants.MODEL_RUN_PORT);
        if (StringUtils.isEmpty(port)) {
            throw new BusException("请先配置模型端口");
        }

        String[] split = port.split(",");
        for (String p : split) {
            try {
                pids.addAll(findPidByPort(Integer.valueOf(p).intValue()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        if (pids.isEmpty()) {
            return false;
        }
        pids = pids.stream().filter(pid -> !pid.equals("0")).collect(Collectors.toList());
        if (!pids.isEmpty()) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean isModelRunning(String port) {
        if (StringUtils.isEmpty(port)) {
            return false;
        }
        List<String> pids = new ArrayList<>();
        try {
            pids.addAll(findPidByPort(Integer.valueOf(port).intValue()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        if (pids.isEmpty()) {
            return false;
        }
        pids = pids.stream().filter(pid -> !pid.equals("0")).collect(Collectors.toList());
        if (!pids.isEmpty()) {
            return true;
        }
        return false;
    }

    /**
     * 根据端口号终止进程
     *
     * @param port
     * @throws IOException
     * @throws InterruptedException
     */
    public static void killProcessByPort(int port) throws IOException, InterruptedException {
        String os = System.getProperty("os.name").toLowerCase();

        // 获取当前 Java 进程 PID
        long currentPid = ProcessHandle.current().pid();

        List<String> pids = findPidByPort(port);

        List<String> pidsNoCurrentPidList = pids.stream().filter(pid -> !pid.equals(String.valueOf(currentPid))).collect(Collectors.toList());
        if (pidsNoCurrentPidList.isEmpty()) {
            return;
        }

        for (String pid : pidsNoCurrentPidList) {
            killProcess(pid, os);
        }
    }

    /**
     * 根据端口号查找进程 ID
     *
     * @param port
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    private static List<String> findPidByPort(int port) throws IOException, InterruptedException {
        String os = System.getProperty("os.name").toLowerCase();
        List<String> command = new ArrayList<>();
        if (os.contains("win")) {
            // Windows: netstat 查找端口对应 PID
            command.add("cmd.exe");
            command.add("/c");
            command.add("netstat -ano | findstr :" + port);
        } else {
            // Linux/macOS: lsof 查找端口对应 PID
            command.add("sh");
            command.add("-c");
            command.add("lsof -i :" + port + " | grep LISTEN");
        }

        Process process = new ProcessBuilder(command).start();
        List<String> pids = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                String pid = parsePidFromLine(line, os);
                if (pid != null && !pid.isEmpty()) {
                    pids.add(pid);
                }
            }
        }
        process.waitFor();
        return pids;
    }

    /**
     * 解析命令输出中的 PID
     *
     * @param line
     * @param os
     * @return
     */
    private static String parsePidFromLine(String line, String os) {
        if (os.contains("win")) {
            // Windows: 提取最后一列的 PID
            String[] parts = line.trim().split("\\s+");
            return parts.length > 4 ? parts[4] : null;
        } else {
            // Linux/macOS: 提取第二列的 PID
            String[] parts = line.trim().split("\\s+");
            return parts.length > 1 ? parts[1] : null;
        }
    }

    /**
     * 终止进程
     *
     * @param pid
     * @param os
     * @throws IOException
     * @throws InterruptedException
     */
    private static void killProcess(String pid, String os) throws IOException, InterruptedException {
        List<String> command = new ArrayList<>();
        if (os.contains("win")) {
            command.add("taskkill");
            command.add("/F");
            command.add("/PID");
            command.add(pid);
        } else {
            command.add("kill");
            command.add("-9");
            command.add(pid);
        }

        Process process = new ProcessBuilder(command).start();
        process.waitFor();
    }

    private static void requestAdminPrivileges() {
        try {
            String jarPath = new File(
                    AssistantServiceImpl.class.getProtectionDomain()
                            .getCodeSource().getLocation().toURI())
                    .getPath();

            String vbs = "Set UAC = CreateObject(\"Shell.Application\")\n"
                    + "UAC.ShellExecute \"cmd.exe\", \"/c java -jar \"\""
                    + jarPath + "\"\" " + "\", \"\", \"runas\", 1";

            File tempScript = File.createTempFile("elevate", ".vbs");
            try (PrintWriter writer = new PrintWriter(tempScript)) {
                writer.println(vbs);
            }

            new ProcessBuilder("wscript", tempScript.getAbsolutePath()).start();
            System.exit(0);
        } catch (Exception ex) {
            System.err.println("提权请求失败: " + ex.getMessage());
        }
    }

    @Override
    public void exec(String path, String invokeSoftwareName, Boolean needAdminPermission, Boolean isBackRunning) {
        File file = new File(path);
        if (StringUtils.isBlank(path) || !file.exists()) {
            throw new BusException(invokeSoftwareName + "路径不存在");
        }

        // 转义路径中的双引号并用双引号包裹路径
        String escapedPath = "\"" + path.replace("\"", "\\\"") + "\"";

        List<String> commandList = new ArrayList<>();
        if (needAdminPermission) {
            commandList.add("powershell.exe");
            commandList.add("-Command");
            commandList.add("Start-Process");
            commandList.add(escapedPath);
            commandList.add("-Verb");
            commandList.add("RunAs");
        } else {
            if (isBackRunning) {
                commandList.add("cmd.exe");
                commandList.add("/c");         // 执行后关闭
                commandList.add("start");
                commandList.add("\"" + invokeSoftwareName + "\""); // 标题
                commandList.add("/B");         // 关键参数：后台运行
                commandList.add(escapedPath);  // 需要运行的命令或程序
            } else {
                commandList.add("cmd.exe");
                commandList.add("/c");
                commandList.add("start");
                commandList.add("cmd.exe");
                commandList.add("/k");
                commandList.add(escapedPath);
            }
        }

        log.info("启动命令: {}", String.join(" ", commandList));

        // 使用 ProcessBuilder 执行
        ProcessBuilder pb = new ProcessBuilder(commandList);
        pb.redirectErrorStream(true); // 合并输出流和错误流
        pb.directory(file.getParentFile()); // 设置工作目录为文件所在目录
        try {
            Process process = pb.start();
            log.info(invokeSoftwareName + "启动成功");
            log.info("进程已启动，PID: {}", process.pid());
            // 消费输出流
            new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                    }
                } catch (IOException e) {
                    log.error("读取输出失败", e);
                }
            }).start();

        } catch (IOException e) {
            throw new RuntimeException(invokeSoftwareName + "启动失败");
        }
    }

    public static void main(String[] args) throws IOException {
        AssistantServiceImpl assistantService = new AssistantServiceImpl();
        assistantService.openFolder("D:\\video/play_log/例子");

//        open("D:\\winning");
    }

}
